/*
 * Copyright © 2022 plgxs.top All Rights Reserved Stranger.
 */

package top.plgxs.admin.utils.rsa;

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.security.KeyFactory;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

import javax.crypto.Cipher;

/**
 * 备注：未考虑分段加密！！！
 */
public class KeyStoreUtil {
	
	public static final String KS_TYPE = "JKS";	// Java密钥库（KeyStore）常见类型
	
	public static final String X509 = "X.509";	// 
	
	/**
	 * 获取密钥库
	 * 
	 * @param path		后缀为.keystore的文件所在路径
	 * @param storePass	密钥库的密码
	 * @return
	 * @throws Exception
	 */
	private static KeyStore getKeyStore(String path, String storePass) throws Exception {
		FileInputStream in = new FileInputStream(path);
		KeyStore ks = KeyStore.getInstance(KS_TYPE);
		ks.load(in, storePass.toCharArray());
		in.close();
		return ks;
	}
	
	/**
	 * 从后缀为.keystore的文件获取私钥
	 * 
	 * @param path		后缀为.keystore的文件所在路径
	 * @param storePass	密钥库的密码
	 * @param alias		别名
	 * @param keypass	别名条目的密码
	 * @return
	 * @throws Exception
	 */
	private static PrivateKey getPrivateKey(String path, String storePass, String alias, String keypass) throws Exception {
		KeyStore ks = getKeyStore(path, storePass);
		PrivateKey privateKey = (PrivateKey) ks.getKey(alias, keypass.toCharArray());
		return privateKey;
	}
	
	/**
	 * 从后缀为.keystore的文件获取公钥
	 * 服务器端使用，但是不常用，服务器端主要使用私钥
	 * @param path		后缀为.keystore的文件所在路径
	 * @param storePass	密钥库的密码
	 * @param alias		别名
	 * @return
	 * @throws Exception
	 */
	private static PublicKey getPublicKey(String path, String storePass, String alias) throws Exception {
		KeyStore ks = getKeyStore(path, storePass);
		PublicKey publicKey = ks.getCertificate(alias).getPublicKey();
		return publicKey;
	}
	
	/**
	 * 从后缀为.cer的文件获取公钥
	 * 客户端使用
	 * @param cerPath	后缀为.cer的文件所在路径
	 * @return
	 * @throws Exception
	 */
	private static PublicKey getPublicKey(String cerPath) throws Exception {
		X509Certificate cer = null;
		CertificateFactory certificateFactory = CertificateFactory.getInstance(X509);
		FileInputStream in = new FileInputStream(cerPath);
		cer = (X509Certificate) certificateFactory.generateCertificate(in);
		in.close();
		
//		System.out.println("读取Cer证书信息...");
//		System.out.println("x509Certificate_SerialNumber_序列号___:" + cer.getSerialNumber());
//		System.out.println("x509Certificate_getIssuerDN_发布方标识名___:" + cer.getIssuerDN());
//		System.out.println("x509Certificate_getSubjectDN_主体标识___:" + cer.getSubjectDN());
//		System.out.println("x509Certificate_getSigAlgOID_证书算法OID字符串___:" + cer.getSigAlgOID());
//		System.out.println("x509Certificate_getNotBefore_证书有效期___:" + cer.getNotAfter());
//		System.out.println("x509Certificate_getSigAlgName_签名算法___:" + cer.getSigAlgName());
//		System.out.println("x509Certificate_getVersion_版本号___:" + cer.getVersion());
//		System.out.println("x509Certificate_getPublicKey_公钥___:" + cer.getPublicKey());
		
		return cer.getPublicKey();
	}
	
	/**
	 * 经过BASE64加密过的私钥
	 * 
	 * @param path		后缀为.keystore的文件所在路径
	 * @param storePass	密钥库的密码
	 * @param alias		别名
	 * @param keypass	别名条目的密码
	 * @return			私钥（String类型）
	 * @throws Exception
	 */
	public static String getStrPrivateKey(String path, String storePass, String alias, String keypass) throws Exception {
		PrivateKey privateKey = getPrivateKey(path, storePass, alias, keypass);
		// BASE64加密
		String strPrivateKey = CoderUtil.encryptBASE64(privateKey.getEncoded());
		return strPrivateKey;
	}
	
	/**
	 * 经过BASE64加密过的公钥
	 * 服务器端使用，但是不常用，服务器端主要使用私钥
	 * @param path		后缀为.keystore的文件所在路径
	 * @param storePass	密钥库的密码
	 * @param alias		别名
	 * @return			公钥（String类型）
	 * @throws Exception
	 */
	public static String getStrPublicKey(String path, String storePass, String alias) throws Exception {
		PublicKey publicKey = getPublicKey(path, storePass, alias);
		// BASE64加密
		String strPublicKey = CoderUtil.encryptBASE64(publicKey.getEncoded());
		return strPublicKey;
	}
	
	/**
	 * 经过BASE64加密过的公钥
	 * 客户端使用
	 * @param cerPath	后缀为.cer的文件所在路径
	 * @return			公钥（String类型）
	 * @throws Exception
	 */
	public static String getStrPublicKey(String cerPath) throws Exception {
		PublicKey publicKey = getPublicKey(cerPath);
		// BASE64加密
		String strPublicKey = CoderUtil.encryptBASE64(publicKey.getEncoded());
		return strPublicKey;
	}
	
	/**
	 * 私钥加密
	 * 服务器端使用
	 * @param privateKey私钥（String类型）
	 * @param data		需要加密的数据
	 * @return			rsa base64加密过的数据
	 * @throws Exception
	 */
	public static String encryptByPrivateKey(String privateKey, String data) throws Exception {
		// BASE64解密
		byte[] pk = CoderUtil.decryptBASE64(privateKey);
		PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(pk);
		KeyFactory kf = KeyFactory.getInstance("RSA");
		// 获取私钥
		PrivateKey priKey = kf.generatePrivate(spec);
		
		// 对数据加密
		Cipher cipher = Cipher.getInstance("RSA");
		cipher.init(Cipher.ENCRYPT_MODE, priKey);
		
		byte[] doFinal = cipher.doFinal(data.getBytes());
		return CoderUtil.encryptBASE64(doFinal);
	}

	/**
	 * 私钥解密   分段
	 * 服务器端使用
	 * @param privateKey私钥（String类型）
	 * @param data		需要解密的数据
	 * @return			String类型的数据
	 * @throws Exception
	 */
	public static String encryptByPrivateKeyFen(String privateKey,String data) {
		int MAX_ENCRYPT_BLOCK = 53;
		 byte[] b = data.getBytes();
			try {
			  int inputLen = b.length;
			  ByteArrayOutputStream out = new ByteArrayOutputStream(); 
			  int offSet = 0; 
			  byte[] cache; 
			  int i = 0; 
			  byte[] pk = CoderUtil.decryptBASE64(privateKey);
				PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(pk);
				KeyFactory kf = KeyFactory.getInstance("RSA");
				// 获取私钥
				PrivateKey priKey = kf.generatePrivate(spec);
				
			  Cipher cipher = Cipher.getInstance("RSA"); 
			  cipher.init(Cipher.ENCRYPT_MODE, priKey); 
			  // 对数据分段解密 
			  while (inputLen - offSet > 0) { 
				if (inputLen - offSet > MAX_ENCRYPT_BLOCK) { 
				  cache = cipher.doFinal(b, offSet, MAX_ENCRYPT_BLOCK); 
				} else { 
				  cache = cipher.doFinal(b, offSet, inputLen - offSet); 
				} 
				out.write(cache, 0, cache.length); 
				i++; 
				offSet = i * MAX_ENCRYPT_BLOCK; 
			  } 
			  byte[] decryptedData = out.toByteArray(); 
			  out.close(); 
			  return CoderUtil.encryptBASE64(decryptedData); 
			} catch (Exception e) {
//			logger.error(e.getMessage());
		}
		return null; 
	}
	/**
	 * 私钥解密
	 * 服务器端使用
	 * @param privateKey私钥（String类型）
	 * @param data		需要解密的数据
	 * @return			String类型的数据
	 * @throws Exception
	 */
	public static String descryptByPrivateKey(String privateKey, String data) throws Exception {
		// BASE64解密私钥
		byte[] pk = CoderUtil.decryptBASE64(privateKey);
		// BASE64解密数据
		
		byte[] text = CoderUtil.decryptBASE64(data);
		PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(pk);
		KeyFactory kf = KeyFactory.getInstance("RSA");
		// 获取私钥
		PrivateKey priKey = kf.generatePrivate(spec);
		
		// 对数据解密
		Cipher cipher = Cipher.getInstance("RSA");
		cipher.init(Cipher.DECRYPT_MODE, priKey);
		
		byte[] doFinal = cipher.doFinal(text);
		return new String(doFinal);
	}
	
	/**
	 * 公钥加密
	 * 客户端使用
	 * @param publicKey	公钥（String类型）
	 * @param data		需要加密的数据
	 * @return			rsa base64加密过的数据
	 * @throws Exception
	 */
	public static String encryptByPublicKey(String publicKey, String data) throws Exception {
		// BASE64解密
		byte[] pk = CoderUtil.decryptBASE64(publicKey);
		X509EncodedKeySpec spec = new X509EncodedKeySpec(pk);
		KeyFactory kf = KeyFactory.getInstance("RSA");
		// 获取公钥
		PublicKey pubKey = kf.generatePublic(spec);
		
		// 对数据加密
		Cipher cipher = Cipher.getInstance("RSA");
		cipher.init(Cipher.ENCRYPT_MODE, pubKey);
		
		byte[] doFinal = cipher.doFinal(data.getBytes());
		return CoderUtil.encryptBASE64(doFinal);
	}
	
	/**
	 * 公钥解密
	 * 客户端使用
	 * @param publicKey	公钥（String类型）
	 * @param data		需要解密的数据
	 * @return			String类型的数据
	 * @throws Exception
	 */
	public static String descryptByPublicKey(String publicKey, String data) throws Exception {
		// BASE64解密私钥
		byte[] pk = CoderUtil.decryptBASE64(publicKey);
		// BASE64解密数据
		byte[] text = CoderUtil.decryptBASE64(data);
		
		X509EncodedKeySpec spec = new X509EncodedKeySpec(pk);
		KeyFactory kf = KeyFactory.getInstance("RSA");
		// 获取公钥
		PublicKey pubKey = kf.generatePublic(spec);
		
		// 对数据解密
		Cipher cipher = Cipher.getInstance("RSA");
		cipher.init(Cipher.DECRYPT_MODE, pubKey);
		
		byte[] doFinal = cipher.doFinal(text);
		return new String(doFinal);
	}
	
}
